package com.sowka.miko.weather.model;

import java.net.URL;
import java.util.Date;

/**
 * 
 * Temperature is stored in both Celsius and Fahrenheit, a design choice for
 * performance over possible redundancy. On instantiation a measure of one unit
 * may be recorded, on access of any unit that's not set the getters will
 * calculate other units where possible. The code enabling this unit agnostic
 * behaviour (temp getters) is pretty ugly, but does the trick.
 * 
 * Temperature getters return Interger.MAX_VALUE in case of undefined value;
 * this is actually safer than returning 0.
 * 
 * @author msowka
 * 
 */
public class Conditions {

	/** Celsius to Fahrenheit conversion ration. */
	private static final float cToFRatio = (float) (9.0 / 5.0);
	
	/** The date at which the conditions were recorder. */
	private Date date;
	/** Textual description of the conditions. */
	private String desc;

	// A value of MAX_VALUE flags the field not having been set, akin to null
	// if these were objects
	private float tempC = Float.MAX_VALUE;
	private float tempF = Float.MAX_VALUE;
	private float lowTempC = Float.MAX_VALUE;
	private float lowTempF = Float.MAX_VALUE;
	private float highTempC = Float.MAX_VALUE;
	private float highTempF = Float.MAX_VALUE;

	private float humidity = Float.MAX_VALUE;
	private URL icon;
	private String wind;
	
	public enum TempUnits {
		CELCIUS("℃"),
		FARENHEIT("℉");

		private final String unitsString;

		TempUnits(String unitsString) {
			this.unitsString = unitsString;
		}

		public String getUnitsString() {
			return unitsString; 
		}
	}

	public Date getDate() {
		return date;
	}

	public void setDate(Date date) {
		this.date = date;
	}

	public String getDesc() {
		return desc;
	}

	public void setDesc(String desc) {
		this.desc = desc;
	}

	public float getTempC() {
		if (tempC < Float.MAX_VALUE)
			return tempC;
		
		if (tempF < Float.MAX_VALUE) {
			tempC = convertToC(tempF);
			return tempC;
		} else {
			return Float.MAX_VALUE;
		}
	}

	public void setTempC(float temp) {
		this.tempC = temp;
	}

	public float getTempF() {
		if (tempF < Float.MAX_VALUE)
			return tempF;
		
		if (tempC < Float.MAX_VALUE) {
			tempF = convertToF(tempC);
			return tempF;
		} else {
			return Float.MAX_VALUE;
		}
	}

	public void setTempF(float temp) {
		this.tempF = temp;
	}

	public float getLowTempC() {
		if (lowTempC < Float.MAX_VALUE)
			return lowTempC;
		
		if (lowTempF < Float.MAX_VALUE) {
			lowTempC = convertToC(lowTempF);
			return lowTempC;
		} else {
			return Float.MAX_VALUE;
		}
	}

	public void setLowTempC(float lowTempC) {
		this.lowTempC = lowTempC;
	}

	public float getLowTempF() {
		if (lowTempF < Float.MAX_VALUE)
			return lowTempF;
		
		if (lowTempC < Float.MAX_VALUE) {
			lowTempF = convertToF(lowTempC);
			return lowTempF;
		} else {
			return Float.MAX_VALUE;
		}
	}

	public void setLowTempF(float lowTempF) {
		this.lowTempF = lowTempF;
	}

	public float getHighTempC() {
		if (highTempC < Float.MAX_VALUE)
			return highTempC;
		
		if (highTempF < Float.MAX_VALUE) {
			highTempC = convertToC(highTempF);
			return highTempC;
		} else {
			return Float.MAX_VALUE;
		}
	}

	public void setHighTempC(float highTempC) {
		this.highTempC = highTempC;
	}

	public float getHighTempF() {
		if (highTempF < Float.MAX_VALUE)
			return highTempF;
		
		if (highTempC < Float.MAX_VALUE) {
			highTempF = convertToF(highTempC);
			return highTempF;
		} else {
			return Float.MAX_VALUE;
		}
	}

	public void setHighTempF(float highTempF) {
		this.highTempF = highTempF;
	}

	private float convertToF(float celsius) {
		return (celsius * cToFRatio) + 32;
	}
	
	private float convertToC(float fahrenheit) {
		return (fahrenheit - 32) / cToFRatio;
	}
	
	public float getHumidity() {
		return humidity;
	}

	public void setHumidity(float humidity) {
		this.humidity = humidity;
	}

	public URL getIcon() {
		return icon;
	}

	public void setIcon(URL icon) {
		this.icon = icon;
	}

	public String getWind() {
		return wind;
	}

	public void setWind(String wind) {
		this.wind = wind;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append(String.format("Date: %tc\n", date));
		if (desc != null) builder.append("Conditions: ").append(desc).append("\n");
		float tempC = getTempC();
		if (tempC < Float.MAX_VALUE) {
			builder.append(String.format("Temperature: %.0fC / %.0fF\n",
					tempC, getTempF()));
		}
		if (humidity < Float.MAX_VALUE) {
			builder.append(String.format("Humidity: %.0f%%\n", humidity));
		}
		if (icon != null) builder.append(String.format("Icon URL: %s\n", icon));
		if (wind != null) builder.append(String.format("Wind: %s\n", wind));
		
		return builder.toString();
	}

}
